package com.xyazm.order.mq.consumer;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.xyazm.common.constants.RedisLockKeyConstants;
import com.xyazm.common.constants.RocketMqConstant;
import com.xyazm.common.enums.OrderTypeEnum;
import com.xyazm.common.exception.BaseBizException;
import com.xyazm.common.logger.LoggerFormat;
import com.xyazm.common.message.PaidOrderSuccessMessage;
import com.xyazm.common.redis.RedisLock;
import com.xyazm.common.utils.JsonUtil;
import com.xyazm.fulfill.domain.param.TriggerFulFillParam;
import com.xyazm.order.dao.OrderInfoDAO;
import com.xyazm.order.domain.entity.OrderInfoDO;
import com.xyazm.order.exception.OrderBizException;
import com.xyazm.order.exception.OrderErrorCodeEnum;
import com.xyazm.order.remote.FulfillRemote;
import com.xyazm.order.service.IOrderFulFillService;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

/**
 * 监听订单支付回调成功后的消息 2.0
 * @Author: xuyou
 * @Date: 2022/6/23 16:52
 */
@Slf4j
@Component
@RocketMQMessageListener(
        topic = RocketMqConstant.ORDER_STATE_CHANGE_EVENT_TOPIC,
        consumerGroup = RocketMqConstant.ORDER_STATUS_CHANGE_EVENT_CONSUMER_GROUP,
        selectorExpression = "paid || sub_paid", // 专门从指定的topic里，监听paied类型的消息
        consumeMode = ConsumeMode.CONCURRENTLY, // 不需要自己去拉取msgs, ORDERLY需要自己先加锁，然后拉取msgs
        messageModel = MessageModel.CLUSTERING, // 负载均衡模式（默认） BROADCASTING 广播模式
        consumeThreadMax = 1
)
public class PaidOrderSuccessListener extends AbstractRocketMqListener {

    @Resource
    private RedisLock redisLock;
    @Autowired
    private IOrderFulFillService orderFulFillService;
    @Autowired
    private OrderInfoDAO orderInfoDAO;
    @Autowired
    private FulfillRemote fulfillRemote;


    @Override
    public void onMessage(String message) {
        PaidOrderSuccessMessage paidOrderSuccessMessage = JsonUtil.json2Object(message, PaidOrderSuccessMessage.class);
        String orderId = paidOrderSuccessMessage.getOrderId();
        OrderInfoDO orderInfoDO = orderInfoDAO.getByOrderId(orderId);
        if (Objects.isNull(orderInfoDO)) {
            throw new OrderBizException(OrderErrorCodeEnum.ORDER_INFO_IS_NULL);
        }

        // 判断是否可以触发履约: 没有子订单并且不是虚拟订单可以履约
        // 无效主单过来是不能触发履约，但是子单支付事件也会过来，触发子单履约
        List<OrderInfoDO> subOrderInfos = orderInfoDAO.listByParentOrderId(orderId);
        if (canTriggerFulfill(orderInfoDO, subOrderInfos)) {
            // 1.加分布式锁+履约前置状态校验防止消息重复消费
            String key = RedisLockKeyConstants.ORDER_FULFILL_KEY + orderId;
            if (!redisLock.tryLock(key)) {
                log.error("order has not acquired lock，cannot fulfill, orderId={}", orderId);
                throw new BaseBizException(OrderErrorCodeEnum.ORDER_FULFILL_ERROR);
            }
            try {
                log.info(LoggerFormat.build()
                        .remark("PaidOrderSuccessListener->onMessage")
                        .data("msg", "修改订单状态为已履约")
                        .data("orderId", orderId)
                        .finish());
                orderFulFillService.triggerOrderFulFill(orderId);
                log.info(LoggerFormat.build()
                        .remark("PaidOrderSuccessListener->onMessage")
                        .data("msg", "触发履约系统->接收订单履约")
                        .data("orderId", orderId)
                        .finish());
                // 构建触发订单履约参数
                TriggerFulFillParam triggerFulFillParam = orderFulFillService.buildTriggerFulFillParam(orderInfoDO);
                // 触发订单履约
                fulfillRemote.triggerFulFill(triggerFulFillParam);
            } finally {
                redisLock.unlock(key);
            }
        }

    }

    /**
     * 判断是否可以触发履约
     */
    private boolean canTriggerFulfill(OrderInfoDO order, List<OrderInfoDO> subOrders) {
        return CollectionUtils.isEmpty(subOrders) && OrderTypeEnum.canFulfillTypes().contains(order.getOrderType());
    }


}
